package com.ala4.oxcafe.boot;

import com.ala4.oxcafe.boot.authentication.CustomAuthenticationConfigurer;
import com.ala4.oxcafe.boot.handler.AuthenticationFailureEntryPoint;
import com.ala4.oxcafe.boot.manager.APIAuthorizationManager;
import com.ala4.oxcafe.boot.manager.UserTokenManager;
import com.ala4.oxcafe.properties.FileProperties;
import com.ala4.oxcafe.properties.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 资源服务配置
 *
 * @author PING
 * @version 1.0
 * @date 2025/3/4 11:14
 */
@Configuration
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http,
                                                                 FileProperties fileProperties,
                                                                 SecurityProperties securityProperties,
                                                                 APIAuthorizationManager<RequestAuthorizationContext> apiAuthorizationManager,
                                                                 UserTokenManager userTokenManager,
                                                                 PermitAllUrlProperties permitAllUrlProperties) throws Exception {
        // 组合所有的公开权限
        PathPatternRequestMatcher[] permitAllUrl = this.getPermitAllUrl(fileProperties, securityProperties, permitAllUrlProperties);

        // 禁用 csrf 与 cors
        http.csrf(AbstractHttpConfigurer::disable);
        http.cors(AbstractHttpConfigurer::disable);
        http.sessionManagement(sessionManage -> sessionManage.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        http.with(new CustomAuthenticationConfigurer<>(userTokenManager), Customizer.withDefaults());

        return http.securityMatcher("/**")
                .authorizeHttpRequests(authorize -> authorize
                        // 这些请求使用匿名访问(匿名访问和permitAll的区别就是已登录用户不能访问匿名URL)
                        .requestMatchers("/test/anonymous").anonymous()
                        // 放行静态资源和不需要鉴权的url
                        .requestMatchers(permitAllUrl).permitAll()
                        // 剩下的都需要鉴权
                        .anyRequest().access(apiAuthorizationManager)
                ).exceptionHandling(exceptionHandling ->
                        exceptionHandling.authenticationEntryPoint(AuthenticationFailureEntryPoint::exceptionHandler)
                                .accessDeniedHandler(AuthenticationFailureEntryPoint::exceptionHandler)
                ).build();
    }

    private PathPatternRequestMatcher[] getPermitAllUrl(FileProperties fileProperties, SecurityProperties securityProperties, PermitAllUrlProperties permitAllUrlProperties) {
        List<String> ignoreUriList = Optional.ofNullable(securityProperties.getIgnoreUriList()).orElse(new ArrayList<>());
        ignoreUriList.add(fileProperties.getStaticAccessPath());
        ignoreUriList.add(securityProperties.getPasswordLoginUrl());
        ignoreUriList.add(securityProperties.getWxLoginUrl());
        ignoreUriList.add(securityProperties.getLogoutUrl());
        ignoreUriList.add(securityProperties.getCaptchaUrl());
        // 错误端点放行
        ignoreUriList.add("/error");
        ignoreUriList.add("/favicon.ico");
        ignoreUriList.addAll(permitAllUrlProperties.getUrls());

        return ignoreUriList.stream()
                .map(url -> PathPatternRequestMatcher.withDefaults().matcher(url))
                .toList()
                .toArray(new PathPatternRequestMatcher[0]);
    }
}
